import numpy
from ..functions import parse_indices, subspace_array


# ====================================================================
#
# PPFileArray object
#
# ====================================================================

class PPFileArray(object):
    ''' 
    
'''
    def __init__(self, **kwargs):
        '''

**Initialization**

:Parameters:

    _filename : str
        The PP file name.

    _file_offset : int
        The start position in the file of the data array.

    _binary_mask : bool, optional

    _add_offset : optional 
         By default the array is assumed to have no offset.

    _FillValue : optional 
                 
    _lbpack : int, optional
         By default the array is assumed to be unpacked.

    _scale_factor : optional 
         By default the  array is assumed to have no scale factor.

    dtype : numpy.dtype
        The numpy data type of the data array.

    ndim : int
        Number of dimensions in the data array.

    shape : tuple
        The data array's dimension sizes.

    size : int
        Number of elements in the data array.

**Examples**

>>> ppfile
<open file 'file.pp', mode 'rb' at 0xc45e00>
>>> a = PPFileArray(_filename=ppfile.name, _file_offset=ppfile.tell(),
                    dtype=numpy.dtype('float32'), shape=(73, 96), size=7008,
                    ndim=2)

'''
        for attr, value in kwargs.iteritems():
            setattr(self, attr, value)

        if '_lbpack' not in kwargs:
            self._lbpack = 0

        if '_binary_mask' not in kwargs:
            self._binary_mask = None
    #--- End: def
 
    def __deepcopy__(self, memo):
        '''
Used if copy.deepcopy is called on the variable.

'''
        return self.copy()
    #--- End: def

    def __getitem__(self, indices):
        '''
x.__getitem__(indices) <==> x[indices]

''' 
        # ------------------------------------------------------------
        # Read the array from the PP file
        # ------------------------------------------------------------
        if not self._lbpack:
            # Unpacked data array
            mm_array = numpy.memmap(self._filename, mode = 'r',
                                    offset = self._file_offset,
                                    dtype  = self.dtype,
                                    shape  = self.shape)
        else:
            # Packed data array
            raise ValueError(
"PP data array is packed (LBPACK=%d) and so can not be accessed (yet ...)" %
self._lbpack)

            mm_array = numpy.memmap(self._filename, mode = 'r',
                                    offset = self._file_offset,
                                    dtype  = self.dtype,
                                    shape  = (self.size,))

            # Now unpack the data array ...
        #--- End: If

        indices = parse_indices(mm_array, indices)

        array = subspace_array(mm_array, indices)

        # ------------------------------------------------------------
        # Convert to a masked array
        # ------------------------------------------------------------
        if hasattr(self, '_FillValue'):
            # _FillValue is set so mask any missing values
            fill_value = self._FillValue
            array = numpy.ma.array(array, mask=(array == fill_value),
                                   fill_value=fill_value, copy=True)
            array.shrink_mask()
        else:
            # _FillValue is not set, so there are no missing values
            array = numpy.ma.array(array, copy=True)

        # Close the file
        del mm_array

        # ------------------------------------------------------------
        # Unpack the array using the scale_factor and add_offset, if
        # either is available
        # ------------------------------------------------------------
        if hasattr(self, '_scale_factor') and self._scale_factor != 1.0:
            array *= self._scale_factor

        if hasattr(self, '_add_offset') and self._add_offset != 0.0:
            array += self._add_offset

        if self._binary_mask:
            array = numpy.ma.where(array!=0, 1, 0)

        # ------------------------------------------------------------
        # Return the array
        # ------------------------------------------------------------
        return array
    #--- End: def

    def __str__(self):
        '''
x.__str__() <==> str(x)

'''
        return '%s%s' % (self.__class__.__name__, self.shape)
    #--- End: def

    def close(self):
        '''

Close the file containing the data array.

If the file is not open then no action is taken.

:Returns:

    None

**Examples**

>>> a.close()

'''
        # An instance references no open files
        pass
    #--- End: def
   
    def copy(self):
        '''

Return a deep copy.

Equivalent to ``copy.deepcopy(a)``.

:Returns:

    out :
        A deep copy.
    
**Examples**

>>> b = a.copy()

'''  
        return type(self)(**self.__dict__)
    #--- End: def

    def copy(self):
        '''

Return a deep copy.

Equivalent to ``copy.deepcopy(a)``.

:Returns:

    out :
        A deep copy.

**Examples**

>>> b = a.copy()

'''
        new = type(self)(**self.__dict__)
    #--- End: def
    
#--- End: class


# ====================================================================
#
# PPFileArrayBounds object
#
# ====================================================================

class PPFileArrayBounds(object):
    '''  
'''
    __slots__ = ('_lower'    ,
                 '_upper'    ,
                 'dtype'     ,
                 'shape'     ,
                 'size'      ,
                 'ndim'      ,
                 )

    def __init__(self, lower, upper):
        '''
'''
        self._lower = lower
        self._upper = upper

        self.dtype = numpy.result_type(lower.dtype, upper.dtype)
        self.shape = lower.shape + (2,)       
        self.size  = lower.size * 2
        self.ndim  = lower.ndim + 1
    #--- End: def
   
    def __getitem__(self, indices):
        '''
x.__getitem__(indices) <==> x[indices]

'''
        # ------------------------------------------------------------
        # Read the upper and lower bounds from the PP file and stick
        # them together
        # ------------------------------------------------------------
        array = numpy.column_stack((self._lower[...],
                                    self._upper[...],))

        indices = parse_indices(array, indices)

        return subspace_array(array, indices)
    #--- End: def

    def __str__(self):
        '''
x.__str__() <==> str(x)

'''
        return '%s%s' % (self.__class__.__name__, self.shape)
    #--- End: def
   
#--- End: class
